
import Foundation

enum Result<T, E> {
    
    /*
    We can't use this at the moment due to a LLVM Error:
    error: unimplemented IR generation feature non-fixed multi-payload enum layout
    LLVM ERROR: unimplemented IRGen feature! non-fixed multi-payload enum layout
    case Success(T)
    case Failure(E)
    */
    
    case Success(@autoclosure() -> T)
    case Failure(@autoclosure() -> E)
    
    func isSuccess() -> Bool {
        switch self {
        case .Success: return true
        default: return false
        }
    }
    
    func isFailure() -> Bool {
        return !self.isSuccess()
    }
    
    func success() -> T? {
        switch self {
        case .Success(let x): return Optional.Some(x())
        default: return nil
        }
    }
    
    func failure() -> E? {
        switch self {
        case .Failure(let e): return Optional.Some(e())
        default: return nil
        }
    }
    
    func map<U>(op: T -> U) -> Result<U, E> {
        switch self {
        case .Success(let x): return .Success(op(x()))
        case .Failure(let x): return .Failure(x())
        }
    }
    
    func mapFailure<F>(op: E -> F) -> Result<T,F> {
        switch self {
        case .Success(let t): return .Success(t)
        case .Failure(let e): return .Failure(op(e()))
        }
    }
    
    func and<U>(res: Result<U, E>) -> Result<U, E> {
        switch self {
        case .Success: return res
        case .Failure(let e): return .Failure(e())
        }
    }
    
    func andThen<U>(op: T -> Result<U, E>) -> Result<U, E> {
        switch self {
        case .Success(let t): return op(t())
        case .Failure(let e): return .Failure(e)
        }
    }
    
    func or(res: Result<T, E>) -> Result<T, E> {
        switch self {
        case .Success: return self
        case .Failure: return res
        }
    }
    
    func orElse<F>(op: E -> Result<T, F>) -> Result<T, F> {
        switch self {
        case .Success(let t): return .Success(t())
        case .Failure(let e): return op(e())
        }
    }
    
    func unwrapOr(optb: T) -> T {
        switch self {
        case .Success(let t): return t()
        case .Failure: return optb
        }
    }
    
    func unwrapOrElse(op: E -> T) -> T {
        switch self {
        case .Success(let t): return t()
        case .Failure(let e): return op(e())
        }
    }
    
    // Fails if the value is an `Failure`, with a custom failure message provided
    // by the `Failure`'s value.
    // caller line && file, otherwise would git the current line where the fatalError is called
    func unwrap(file: StaticString = __FILE__, line: UWord = __LINE__) -> T {
        switch self {
        case .Success(let x): return x()
        case .Failure(let e): fatalError("unexpectedly found a Failure while unwrapping a Result value", file:file, line:line)
        }
    }
    
    // Fails if the value is an `Success`, with a custom failure message provided
    // by the `Success`'s value.
    // caller line && file, otherwise would git the current line where the fatalError is called
    func unwrapError(file: StaticString = __FILE__, line: UWord = __LINE__) -> E {
        switch self {
        case .Failure(let e): return e()
        case .Success(let x): fatalError("unexpectedly found a Success while unwrapping a Result value", file:file, line:line)
        }
    }
    
}

extension Result : Printable {
    var description : String {
        switch self {
        case .Success(let x): return ".Success(\(x()))"
        case .Failure(let e): return ".Failure(\(e()))"
        }
    }
}
